from functools import wraps
from fastapi import HTTPException, Depends, Request, FastAPI
from typing import Optional, Dict, List, Callable
import os
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select
from app.models.db import UserRole, Role, get_async_session, User
import inspect
from starlette.middleware.base import BaseHTTPMiddleware
from app.models.user import UserManager, auth_backend, current_active_user, fastapi_users
from jose import jwt
import logging

logger = logging.getLogger(__name__)

# 添加一个排除列表，这些路径不需要权限检查
PERMISSION_EXEMPT_PATHS = [
    # 移除"/"根路径，确保API调用能正确经过权限验证
    "/dk/auth/jwt/login",
    "/dk/auth/send-sms-code",
    "/dk/auth/sms-login",
    "/embed-chat",
    "/docs",
    "/openapi.json",
    "/redoc",
    "/favicon.ico",
    "/static/",  # 使用/结尾，匹配所有静态资源
    "/dk/api/public/health"
]

# 添加普通用户可以访问的API路径前缀
USER_ALLOWED_PATH_PREFIXES = [
    "/dk/sessions/",  # 普通用户可以访问sessions下的所有接口
    "/dk/users/",  # 普通用户可以访问users下的所有接口
    "/dk/knowledge/",  # 普通用户可以访问knowledge-bases下的所有接口
]

async def get_user_role_codes(user_id: str, session: AsyncSession) -> str:
    """
    获取用户角色代码，多个角色用逗号隔开
    
    参数:
        user_id (str): 用户ID
        session (AsyncSession): 数据库会话
        
    返回:
        str: 角色代码，多个用逗号隔开
    """
    # 查询用户所有角色
    query = select(Role.name).join(
        UserRole, 
        UserRole.role_id == Role.id
    ).where(
        UserRole.user_id == user_id
    )
    
    result = await session.execute(query)
    role_names = result.scalars().all()
    
    # 将角色名称用逗号连接
    return ",".join(role_names) if role_names else ""

# 添加便于在API中使用的依赖函数
async def get_current_user_roles(
    current_user: User = Depends(),
    session: AsyncSession = Depends(get_async_session)
) -> str:
    """
    获取当前用户的角色代码，可在API路由中直接作为依赖使用
    
    用法:
    ```
    @router.get("/some-endpoint")
    async def some_endpoint(
        user_roles: str = Depends(get_current_user_roles)
    ):
        # 使用 user_roles...
    ```
    
    参数:
        current_user: 当前用户，通过依赖注入获取
        session: 数据库会话，通过依赖注入获取
        
    返回:
        str: 角色代码，多个用逗号隔开
    """
    if not current_user or not current_user.id:
        raise HTTPException(status_code=401, detail="需要身份验证")
    
    return await get_user_role_codes(str(current_user.id), session)

def get_api_permission_level(func_name: str) -> int:
    """
    获取API的权限级别
    
    参数:
        func_name (str): 函数名称
        
    返回:
        int: 权限级别
            0: 仅管理员可访问
            1: 所有用户都可访问（包括管理员和普通用户）
    """
        
    # 从配置字典获取
    return DEFAULT_API_PERMISSIONS.get(func_name, DEFAULT_API_PERMISSIONS["default"])

class AutoPermissionMiddleware(BaseHTTPMiddleware):
    """
    自动权限检查中间件
    
    无需显式添加装饰器，自动对所有API请求进行权限检查
    """
    def __init__(self, app):
        """初始化中间件"""
        super().__init__(app)
        self.app = app  # 存储FastAPI应用引用
        
    async def dispatch(self, request: Request, call_next):
        """中间件处理函数"""
        # 检查路径是否在豁免列表中 - 这些路径不需要任何认证
        path = request.url.path
        
        # 过滤掉明显的恶意请求或无效路径
        suspicious_extensions = ['.asp', '.aspx', '.php', '.jsp', '.cgi']
        suspicious_paths = ['/a/',  '/wp-admin', '/xmlrpc.php', '/config', '/api/v1/']
        
        # 检查文件扩展名
        if any(path.lower().endswith(ext) for ext in suspicious_extensions):
            from fastapi import Response
            return Response(status_code=404, content="Not Found")
            
        # 检查可疑路径模式
        if any(path.lower().startswith(sus_path) for sus_path in suspicious_paths):
            from fastapi import Response
            return Response(status_code=404, content="Not Found")
        
        # 检查是否是根路径或静态资源路径
        if path == "/" or path.startswith("/static/") or path.startswith("/public/"):  # 显式允许根路径和静态资源路径
            return await call_next(request)
        
        # 添加对特定HTML页面的直接访问支持
        if path.endswith(".html"):
            return await call_next(request)
            
        # 检查是否是豁免路径
        if any(path.startswith(exempt_path) for exempt_path in PERMISSION_EXEMPT_PATHS):
            return await call_next(request)
        
        # 检查是否是静态资源（CSS、JS、图片等）
        if path.endswith(('.js', '.css', '.png', '.jpg', '.jpeg', '.gif', '.svg', '.ico', '.woff', '.woff2', '.ttf', '.eot')):
            return await call_next(request)
            
        # 处理前端路由: /chat/开头的路径所有人可访问
        if path.startswith("/chat/"):
            return await call_next(request)
        try:
            # 创建数据库会话
            db_generator = get_async_session()
            db = await anext(db_generator)
            
            try:
                # 如果是swagger文档请求，跳过权限检查
                if request.headers.get("referer","").endswith("docs"):                                                                                                          
                    return await call_next(request)
             
                # 先尝试从Authorization header获取token
                token = request.headers.get("Authorization", "").replace("Bearer ", "")
                user_id = None
                current_user = None
                
                if token:
                    # 如果有token，使用token获取用户ID
                    user_id = await UserManager.decode_token_and_get_user(token)
                    if user_id:
                        # 根据用户ID从数据库查询用户
                        query = select(User).where(User.id == user_id)
                        result = await db.execute(query)
                        current_user = result.scalar_one_or_none()
                else:
                    # 如果没有token，尝试从cookie中获取username
                    username = request.cookies.get("username")
                    if username:
                        # 根据用户名从数据库查询用户
                        query = select(User).where(User.username == username)
                        result = await db.execute(query)
                        current_user = result.scalar_one_or_none()
                
                if not current_user:
                    raise HTTPException(status_code=401, detail="用户不存在或未登录,请登录后重试")
                
                # 判断用户类型和请求路径
                if not current_user.is_superuser:  # 不是管理员
                    # 处理/admin/路径 - 仅管理员可访问
                    if path.startswith("/admin/"):
                        raise HTTPException(
                            status_code=403, 
                            detail="权限不足，只有管理员才能访问管理页面"
                        )
                        
                    # 检查请求路径是否在普通用户允许的路径列表中
                    is_allowed = False
                    for allowed_prefix in USER_ALLOWED_PATH_PREFIXES:
                        if request.url.path.startswith(allowed_prefix):
                            is_allowed = True
                            break
                    
                    if not is_allowed:
                        raise HTTPException(
                            status_code=403, 
                            detail="权限不足，普通用户只能访问会话相关接口"
                        )
                
                # 将用户存储到request.state
                request.state.user = current_user
                
                # 继续处理请求
                return await call_next(request)
            finally:
                # 关闭数据库会话
                await db.close()
            
        except Exception as e:
            # 记录异常日志
            
            # 如果发生异常，返回适当的状态码
            if isinstance(e, HTTPException):
                # 对于HTTPException，直接创建Response避免再次抛出异常
                from fastapi import Response
                import json
                return Response(
                    status_code=e.status_code,
                    content=json.dumps({"detail": e.detail}),
                    media_type="application/json"
                )
            else:
                # 对于其他异常，返回500错误
                from fastapi import Response
                import json
                return Response(
                    status_code=500,
                    content=json.dumps({"detail": "内部服务器错误"}),
                    media_type="application/json"
                )
            